function MapManager(/*Game*/ game){
	this.map = this.getMap();
	this.name = "LEVEL 1";
	this.player = new Player(vec2(3, 1), this);
	this.game = game;
	
	this.player.direction = 3;
	this.player.act();
	
	this.renderSize = vec2(632, 312);
	this.renderSizeH = vec2(316, 156);
	this.distScale = [1.2,0.7,0.4,0.2,0.1,0.05,0.0];
	
	this.instances = [];
	this.instances.push(new Chest(vec2(5,3), this));
	this.instances.push(new Lader(vec2(5,1), this, false));
	this.instances.push(new Enemy(vec2(1,1), this, EnemyFactory.getEnemy("rat")));
	this.instances.push(new Enemy(vec2(5,1), this, EnemyFactory.getEnemy("bat")));
	
	Console.addMessage("You woke up in a locked cell.", "u1", "yellow");
}

MapManager.prototype.getMap = function(){
	var map = [
		[0,1,0,1,0,1,0],
		[1,0,1,0,1,0,1],
		[1,0,1,0,4,0,1],
		[1,0,2,0,3,0,1],
		[1,0,1,0,1,0,1],
		[1,0,2,0,0,0,1],
		[0,1,0,4,1,1,0]
	];
	
	return map;
};

MapManager.prototype.isSolid = function(/*Vec2*/ position){
	if (!this.map[position.b]) return 1;
	
	var t = this.map[position.b][position.a];
	var tile = TileFactory[t];
	
	if (!tile) return false;
	if (tile.solid) return tile.solid;
	
	return 0;
};

MapManager.prototype.getWallDescription = function(/*Vec2*/ position){
	if (!this.map[position.b]) return null;
	
	var t = this.map[position.b][position.a];
	var tile = TileFactory[t];
	
	if (!tile) return false;
	if (tile.desc) return tile.desc;
	
	return null;
};

MapManager.prototype.getDirectionVector = function(/*Int*/ direction){
	var ret = null;
	switch (direction){
		case 0: ret = vec2(1, 0); break;
		case 1: ret = vec2(0, -1); break;
		case 2: ret = vec2(-1, 0); break;
		case 3: ret = vec2(0, 1); break;
	}
	
	return ret;
};

MapManager.prototype.getInstancesAt = function(/*Int*/ x, /*Int*/ y){
	var ret = [];
	var addE = false;
	
	for (var i=0,len=this.instances.length;i<len;i++){
		var ins = this.instances[i];
		if (ins.position.equals(x, y)){
			if (!ins.isEnemy || (ins.isEnemy && !addE)){
				ret.push(ins);
				if (ins.isEnemy) addE = true;
			}
		}
	}
	
	return ret;
};

MapManager.prototype.getEnemyAt = function(/*Int*/ x, /*Int*/ y){
	for (var i=0,len=this.instances.length;i<len;i++){
		if (this.instances[i].position.equals(x, y) && this.instances[i].isEnemy){
			return this.instances[i];
		}
	}
	
	return null;
};

MapManager.prototype.castRay = function(/*Vec2*/ position, /*Int*/ direction){
	var dir = this.getDirectionVector(direction);
	var pos = position.clone();
	
	var matches = [];
	for (var i=0;i<5;i++){
		var sol = this.isSolid(pos);
		if (sol == 1 || sol == 2){
			var d = this.getWallDescription(pos);
			if (d != null) matches.push(d);
			i = 5;
			continue;
		}else{
			var ins = this.getInstancesAt(pos.a, pos.b);
			if (ins.length > 0){
				for (var j=0,jlen=ins.length;j<jlen;j++){
					matches.push(ins[j].name);
				}
			}
		}
		
		pos.sum(dir);
	}
	
	return matches;
};

MapManager.prototype.castAttack = function(attacker, defender){
	if (defender.spd > attacker.spd + 20){
		Console.addMessage("Missed!", "attack3", "yellow");
		return;
	}else if (defender.spd > attacker.spd + 10){
		var dice = Math.iRandom(100);
		if (dice > attacker.luk){
			Console.addMessage("Missed!", "attack3", "yellow");
			return;
		}
	}
	
	var dmg = attacker.str;
	if (attacker.weapon) dmg += attacker.weapon.str;
	if (attacker.stm){ 
		var hgry = Math.round((100 - attacker.stm) / 10);
		if (hgry > 0) dmg -= hgry;
	}
	
	var dfs = defender.dfs + Math.iRandom(defender.dfs * 0.2);
	if (defender.armour) dfs += defender.armour.dfs;
	
	var dice = Math.iRandom(100);
	if (dice <= attacker.luk){
		dmg *= 1.5;
	}
	
	dmg -= dfs;
	
	if (dmg <= 0){
		Console.addMessage("No damaged caused!", "attack3", "yellow");
		return;
	}
	
	dmg = Math.round(dmg);
	Console.addMessage("Hit " + dmg + " points!", "attack3", "red");
	
	defender.hp -= dmg;
	if (defender.hp <= 0){
		Console.addMessage(defender.name + " died!", "attack4", "red");
	}
};

MapManager.prototype.drawFrontExtra = function(/*Int*/ distance, /*Int*/ tile){
	var rh = this.renderSizeH;
	var tile = TileFactory[tile];
	if (!tile) return;
	if (!tile.drawFront) return;
	
	tile.drawFront(distance, rh, this);
};

MapManager.prototype.drawLateralExtra = function(/*Int*/ distance, /*Int*/ position, /*Int*/ tile){
	var rh = this.renderSizeH;
	var tile = TileFactory[tile];
	if (!tile) return;
	if (!tile.drawLateral) return;
	
	tile.drawLateral(distance, rh, position, this);
};

MapManager.prototype.drawFrontWall = function(/*Int*/ distance, /*bool*/ closeL, /*bool*/ closeR){
	var rh = this.renderSizeH;
	var x1, y1, x2, y2, s;
	
	s = this.distScale[distance];
	x1 = rh.a - (rh.a * s);
	y1 = rh.b - (rh.b * s);
	x2 = rh.a + (rh.a * s);
	y2 = rh.b + (rh.b * s);
	
	this.game.drawLine(x1,y1,x2,y1);
	this.game.drawLine(x1,y2,x2,y2);
	if (closeL) this.game.drawLine(x1,y1,x1,y2);
	if (closeR) this.game.drawLine(x2,y1,x2,y2);
};

MapManager.prototype.drawLateralWall = function(/*Int*/ distance, /*Int*/ position, /*bool*/ closeL, /*bool*/ closeR, /*bool*/ front){
	var rh = this.renderSizeH;
	var x1, y1, x2, y2, y3, y4, s1, s2;
	
	s1 = this.distScale[distance];
	s2 = this.distScale[distance + 1];
	
	x1 = rh.a + (rh.a * s1 * position);
	x2 = rh.a + (rh.a * s2 * position);
	
	y1 = rh.b - (rh.b * s1);
	y2 = rh.b - (rh.b * s2);
	
	y3 = rh.b + (rh.b * s1);
	y4 = rh.b + (rh.b * s2);
	
	if (front){
		this.game.drawLine(x1,y2,x2,y2);
		this.game.drawLine(x1,y4,x2,y4);
		if (closeL) this.game.drawLine(x1,y2,x1,y4);
		if (closeR) this.game.drawLine(x2,y2,x2,y4);
	}else{
		this.game.drawLine(x1,y1,x2,y2);
		this.game.drawLine(x1,y3,x2,y4);
		if (closeL) this.game.drawLine(x1,y1,x1,y3);
		if (closeR) this.game.drawLine(x2,y2,x2,y4);
	}
};

MapManager.prototype.getSiblingTile = function(/*Int*/ x, /*Int*/ y, /*Int*/ direction){
	direction = (direction + 4) % 4;
	var vec = this.getDirectionVector(direction);
	if (!this.map[y + vec.b]) return 0;
	var t = this.map[y + vec.b][x + vec.a];
	return t;
};

MapManager.prototype.worldcast = function(){
	var ctx = this.game.getCtx();
	var p = this.player.position;
	var dir = this.player.direction;
	var fwd = this.getDirectionVector(dir);
	
	var r = this.renderSize;
	
	ctx.strokeStyle = "white";
	
	var doNext = true;
	var drawInstances = [];
	for (var i=0;i<6;i++){
		if (!doNext) break;
		
		var cx = p.a + fwd.a * i;
		var cy = p.b + fwd.b * i;
		
		
		var ins = this.getInstancesAt(cx, cy);
		if (ins.length > 0){
			for (var k=0,klen=ins.length;k<klen;k++){
				var inst = {ins: ins[k], depth: i};
				var added = false;
				for (var j=0,jlen=drawInstances.length;j<jlen;j++){
					if (i > drawInstances[j].depth){
						drawInstances.splice(j,0,inst);
						j = jlen;
						added = true;
					}
				}
				
				if (!added) drawInstances.push(inst);
			}
		}
			
				
		var t = this.map[cy][cx];
		var tl = this.getSiblingTile(cx, cy, (dir + 1));
		var tr = this.getSiblingTile(cx, cy, (dir - 1));
		
		var cl, cr;
		
		if (t >= 1){
			cl = (tl == 0);
			cr = (tr == 0);
			this.drawFrontWall(i, cl, cr);
			this.drawFrontExtra(i, t);
			
			doNext = false;
		}
		
		var ncx = p.a + fwd.a * (i + 1);
		var ncy = p.b + fwd.b * (i + 1);
		var pcx = p.a + fwd.a * (i - 1);
		var pcy = p.b + fwd.b * (i - 1);
		
		if (tl >= 1){
			var ptl = this.getSiblingTile(pcx, pcy, (dir + 1));
			var ntl = this.getSiblingTile(ncx, ncy, (dir + 1));
			
			cl = (ptl == 0);
			cr = (ntl == 0);
		
			if (t == 0){ 
				this.drawLateralWall(i, -1, cl, cr);
				this.drawLateralExtra(i, -1, tl); 
			}
			if (ptl == 0 && i > 0) this.drawLateralWall(i - 1, -1, false, false, true);
		}
		
		if (tr >= 1){
			var ptr = this.getSiblingTile(pcx, pcy, (dir - 1));
			var ntr = this.getSiblingTile(ncx, ncy, (dir - 1));
			
			cl = (ptr == 0);
			cr = (ntr == 0);
			
			if (t == 0){ 
				this.drawLateralWall(i, 1, cl, cr);
				this.drawLateralExtra(i, 1, tr);
			}
			if (ptr == 0 && i > 0) this.drawLateralWall(i - 1, 1, false, false, true);
		}
	}
	
	if (drawInstances != null && drawInstances.length > 0){
		for (var i=0,len=drawInstances.length;i<len;i++){
			var ins = drawInstances[i];
			ins.ins.draw(ctx, this.distScale[ins.depth]);
		}
	}
};

MapManager.prototype.loop = function(){
	if (this.player.action){
		for (var i=0,len=this.instances.length;i<len;i++){
			var ins = this.instances[i];
			if (!ins) continue;
			
			if (ins.loop){ ins.loop(); }
			
			if (!ins.inMap){
				this.instances.splice(i,1);
				i--;
			}
		}
		
		this.game.clearScreen();
		this.worldcast();
	}
	
	this.player.loop();
};
